#ifndef __BLOCK_QUEUE__
#define __BLOCK_QUEUE__

#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

#define MAX_CAPACITY 1000
#define DEFAULT_TIMEOUT 3

template <class T>
class BlockQueue
{
public:
    BlockQueue(int initcapacity = MAX_CAPACITY);
    
    ~BlockQueue();

    void Push(const T &item);

    bool Pop(T &item);
    bool Pop(T &item,int timeout);

    void Clear();

    void flush();

    T front();

    size_t capacity();
    size_t size();

    bool empty();
    bool full();

    void Close();

private:
    std::queue<T> Queue_;
    size_t capacity_;
    size_t size_;
    std::mutex Mtx_;
    std::condition_variable isFull_;
    std::condition_variable isEmpty_;
    std::condition_variable isExit_;
    bool isClose_;
};

#include "BlockQueue.h"

template <class T>
BlockQueue<T>::BlockQueue(int initcapacity) : capacity_(initcapacity),
                                              isClose_(false),
                                              size_(0)
{
}

template <class T>
BlockQueue<T>::~BlockQueue()
{
    Close();
}

template <class T>
void BlockQueue<T>::Push(const T &item)
{
    std::unique_lock<std::mutex> lock(Mtx_);
    while (size_ >= capacity_)
    {
        isFull_.wait(lock);
        if (isClose_)
            return;
    }
    Queue_.push(item);
    size_++;
    isEmpty_.notify_all();
}

template <class T>
bool BlockQueue<T>::Pop(T &item)
{
    std::unique_lock<std::mutex> lock(Mtx_);
    while (empty())
    {
        isEmpty_.wait(lock);
        if (isClose_)
            return false;
    }
    item = front();
    Queue_.pop();
    size_--;
    isFull_.notify_all();
    return true;
}

template <class T>
bool BlockQueue<T>::Pop(T &item, int timeout)
{
    timeout = DEFAULT_TIMEOUT;
    std::unique_lock<std::mutex> lock(Mtx_);
    while (empty())
    {
        if (std::cv_status::timeout == isEmpty_.wait_for(lock, std::chrono::seconds(timeout)))
            return false;
        if (isClose_)
            return false;
    }
    item = front();
    Queue_.pop();
    size_--;
    isFull_.notify_all();
    return true;
}

template <class T>
void BlockQueue<T>::Clear()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    while (Queue_.size())
        Queue_.pop();
    size_ = 0;
}

template <class T>
void BlockQueue<T>::flush()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    isEmpty_.notify_all();
}

template <class T>
T BlockQueue<T>::front()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    return Queue_.front();
}

template <class T>
size_t BlockQueue<T>::capacity()
{
    return capacity_;
}

template <class T>
size_t BlockQueue<T>::size()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    return size_;
}

template <class T>
bool BlockQueue<T>::empty()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    return size_ == 0;
}

template <class T>
bool BlockQueue<T>::full()
{
    std::unique_lock<std::mutex> lock(Mtx_);
    return size_ == capacity_;
}

template <class T>
void BlockQueue<T>::Close()
{
    isClose_ = true;
    std::unique_lock<std::mutex> lock(Mtx_);
    Clear();
    isFull_.notify_all();
    isEmpty_.notify_all();
}


#endif